#include "mmu.h"


/**
* this function initilize a singel ipt record
* the pointers next and prev is init to -1.
 * */
void initIptRecord(iptRecord* rec,int pid, int logicPage){
	rec->pid = pid;
	rec->logicPageNum = logicPage;
	rec->dirtyBit = 0;
	rec->referenceBit = 0;
	rec->next = -1;
	rec->prev = -1;
	rec->ageReg = 0;
}

void insertIptRecord(int pid, int pageNum, int recordIndexToRemove){
//	pthread_mutex_lock(&iptMutex);
	int next = ipt[recordIndexToRemove].next;	//old element next
	int prev = ipt[recordIndexToRemove].prev;	//old element prev
	int oPid = ipt[recordIndexToRemove].pid;	//old pid
	int oPageNum = ipt[recordIndexToRemove].logicPageNum;	//old page number
	int firstElement = hat[hash(pid + pageNum)];	//the first index where the new record should have been hashed to
	if (oPid != -1){
		//deleting the row recordIndexToRemove  in the ipt
		if (next == -1  && prev == -1){
			hat[hash(oPageNum + oPid)] = -1;
		}
		else if ( next == -1 && prev != -1 ){
			ipt[prev].next = -1;
		}
		else if (next != -1 && prev == -1){
			hat[hash(oPageNum + oPid)] = next;
			ipt[next].prev = -1;
		}
		else{
			ipt[prev].next = next;
			ipt[next].prev = prev;
		}
	}
	initIptRecord(&ipt[recordIndexToRemove],pid,pageNum);
	if (firstElement != -1 && firstElement != recordIndexToRemove) 	//this element is going to be a head in a new chain
	{
		ipt[recordIndexToRemove].next = firstElement;
		ipt[firstElement].prev = recordIndexToRemove;

	}
	hat[hash(pid + pageNum)] = recordIndexToRemove;


}


/**
 * this function initilize the entier inverted page table,
 *  it calls the initIptRecord for every row in the table.
 * */
void initIpt(){
	ipt = malloc(sizeof(iptRecord)*NumOfPagesInMM);
	int i;
	for (i = 0; i < NumOfPagesInMM; i++ ){
		initIptRecord(&ipt[i],-1,-1);
	}
	//pthread_mutex_init(&iptMutex,NULL);
}

void killIpt()
{
	FREE(ipt)
}

/**
 * this function initilize the HAT table with the value -1;
 * */
void initHat(){
	hat = malloc(sizeof(int)*NumOfPagesInMM);
	int i;
	for (i = 0; i < NumOfPagesInMM ; i++){
		hat[i] = -1;
	}
}

void killHat()
{
	FREE(hat);
}


int hash(int num){
	return num%NumOfPagesInMM;
}

int hatGetIptFirstRec(int pid, int vPageNum){
	return hash(vPageNum + pid);
}

/**
 * this function is looking for a certain page ,
 *  returns -1 if the page is not in the main memory
 * outherwise it returns the index in the main memory
 * */
int iptGetPageIndexInMM(int pid, int logicalNum){
//	pthread_mutex_lock(&iptMutex);
	int i = hatGetIptFirstRec(pid,logicalNum);
	if (hat[i] == -1) return -1;
	iptRecord rec = ipt[hat[i]];
	i = hat[i];
	while (rec.pid != pid || rec.logicPageNum !=logicalNum){
		i = rec.next ;
		if (i == -1) break;
		rec = ipt[i];
	}
	//sem_wait(&(rec.sem));
//	pthread_mutex_unlock(&iptMutex);
	return i;
}

int getNextFreePageInMM(){
	int i;
	int oldest = 0;
	for (i = 1; i < NumOfPagesInMM; i++){
		if (ipt[i].ageReg < ipt[oldest].ageReg){
			oldest = i;
		}
		else if (ipt[i].ageReg == ipt[oldest].ageReg  &&
				ipt[i].dirtyBit<ipt[oldest].dirtyBit){
			oldest = i;
		}
	}
	return oldest;
}



void setNewAge(int iptIndex){
	ipt[iptIndex].ageReg = 0x10000000;
}
void printMMUTable(){
	int i;
	for (i = 0; i < NumOfPagesInMM; i++) {
		printIPTRecord(ipt[i]);
	}
}

void printIPTRecord(iptRecord rec){
	if (rec.pid == -1) printf("(free)");
	else {
		printf("(%d, %d, %d, %d, %d, %d)",rec.pid,rec.logicPageNum, rec.dirtyBit,
				rec.referenceBit, rec.prev, rec.next);
	}
}

/* Print n as a binary number */
void printbitssimple(int n) {
	unsigned int i;
	i = 1<<(sizeof(n) * 8 - 1);

	while (i > 0) {
		if (n & i)
			printf("1");
		else
			printf("0");
		i >>= 1;
	}
}

void printRegisters(){
	int i;
		for (i = 0; i < NumOfPagesInMM; i++) {
			//printf("%u, ",ipt[i].ageReg);
			printbitssimple(ipt[i].ageReg);
			printf("\n");
		}
}
void printHatRec(int index){
	if (hat[index] == -1){
		printf("{empty}\n");
		return;
	}
	int currIndex = hat[index];
	while (currIndex != -1){
		iptRecord rec = ipt[currIndex];
		printf("{%d}",currIndex);
		currIndex = rec.next;
	}
	printf("\n");

}
void printHat(){
	int i;
	for (i = 0; i < NumOfPagesInMM; i++){
		printHatRec(i);
	}
}


